查看原文
其他

小白训练营 | Android WebView安全浅谈

xvale vivo千镜 2022-11-05

概述:WebView是Android系统常用组件,也是安全问题的重灾区。本文主要对WebView中白名单绕过的安全问题进行分析和介绍,为研发同学补齐知识盲区,避免安全问题的发生。


WebView简介


WebView是一款功能强大的组件,主要用于展示网页内容,核心功能包括:加载和渲染H5页面、本地(Android Java层)与 H5页面实现交互及调用等。由于WebView功能的强大,所以开发者无需深入了解浏览器引擎,仅通过WebView就可实现丰富网页内容的渲染工作。移动端WebView主要分Android 和 iOS 两个阵营,本文主要介绍Android系统的WebView。Android系统的WebView包含Android WebKit WebView和Chromium WebView。在讨论WebView时,又引入WebKit、chromium等内容,所以有必要先讨论清楚它们,然后再讨论WebView。


列王纷争——浏览器分久必合


虽然市面上浏览器众多,但谷歌、微软、火狐浏览器的市场占有率超80%,呈现三足鼎立态势。


 

图一 浏览器混战示意图


浏览器是进入Web的门户,所以成为各大厂商抢占Web的入口,造成浏览器品种繁多,良莠不齐。但无论哪家的Web浏览器,都包含如下三大引擎:浏览器引擎、渲染引擎、JavaScript引擎。浏览器整体架构如下:


 

图二 浏览器架构图


浏览器引擎

协调(主控)UI和渲染引擎,在两者间传输指令。提供对渲染引擎的高级接口,一方面提供初始化加载URL的方法,以及其他高级的浏览器动作(如刷新、向前、退后等)的方法;另一方面也为用户接口提供各种与错误、加载进度相关的消息。


渲染引擎

为指定的URL提供可视化的展示。主要负责解析JavaScript、HTML、Xml。其中关键的组件是HTML解析器。值得注意是:不同的浏览器使用的渲染引擎不尽相同,具体可参考表一。


JavaScript引擎

解释和运行网站上的JS代码,将执行结果传输给渲染引擎用于展示。


浏览器

渲染引擎

JS引擎

IE

Trident (MSHTML)

JScript/Chakra

Microsoft Edge

EdgeHTML

Chakra

Firefox

Gecko

SpiderMonkey

Safari

WebKit

JavaScriptCore

Chrome

Blink

V8

Opera

Presto

Carakan

表一 厂家浏览器引擎技术

上表展示常用浏览器和引擎间的对应关系,但这里不得不提另外一个开源浏览器Chromium,越来越多的浏览器都是基于Chromium内核的,如Google的Chrome,微软新版Microsoft Edge,Opera 15等。

至此,回到本文的重点内容WebView,基于WebKit和Chromium两个版本的WebView,是指内核技术不一样,即对应的渲染引擎、JS引擎不一样,具体可参考表一。而WevView本身则可类比为一款简易版的浏览器。


沙场练兵——WebView使用指南


上文所述,WebView可认为是简易版的浏览器,功能强大,除可直接加载URL,还可使用本地HTML文件,甚至可通过JavaScript实现HTML和原生APP的互调。任何事情都有两面性,功能强大很多情况下都意味安全性差,所以WebView也是移动端安全问题的重灾区,研发人员稍不注意就引入安全问题。

在讨论WebView的安全问题之前,先讨论WebView的正常使用。




(1)两个重要的类


WebViewClient和WebChromeClient类通常在使用WebView时,都会接触WebViewClient和WebChromeClient两个类,那么这两个类主要功能是什么呢?

WebViewClient位于框架层WebKit目录,主要功能是辅助WebView处理各种通知、请求事件等,包含shouldOverrideUrlLoading、onPageStarted、onPageFinished等重要方法。


onPageStarted:页面开始加载时,WebViewClient提供给用户的回调方法onPageFinished:页面完成加载时,WebViewClient提供给用户的回调方法shouldOverrideUrlLoading:当前WebView存在新URL被加载时,WebViewClient提供给用户的回调方法,比如当前页面存在重定向,则重定向URL被加载时,会进入该方法。


WebChromeClient位于框架层WebKit目录,主要功能是辅助WebView处理JS的对话框、网站图标、网站title、加载进度等,主要包含onReceivedTitle、onReceivedIcon、onJsAlert、onProgressChanged等重要方法。


onReceivedTitle:获取网页标题onProgressChanged:加载进度的回调方法onReceivedIcon:获取网页icon




(2)JS与原生APP的互通


WebView内置addJavascriptInterface方法,该方法通过将Java对象注入到WebView,实现JS的Java的互通。




(3)WebView加载URL


WebView使用流程包括设置ChromeClient、ViewClient、开启JS、设置JSBridage、加载URL等,流程如下图所示:


 

至此,WebView的使用方法已介绍完毕,可以欢快的加载网页了。不过事情肯定不会这么美好,安全的问题即将浮出水面。


危机四伏——潜在的安全问题


按照上述方法,研发小白同学完成功能,认为可以早早下班了,我们只能说还是太年轻。如果小白在学习WebView之前,简单去网上搜索相关的漏洞,肯定会发现一堆的安全问题,如中间人攻击、恶意JS攻击设备、跨域访问本地资源等,而很多攻击可能仅在浏览一个恶意站点,或是扫描一个恶意的二维码时,就悄然发生了,然后你的信息就莫名被窃取,或者手机莫名下载、安装恶意应用,手机直接沦为肉鸡。看到这里,我相信小白一定没了下班的欲望,你永远不知道安全漏洞和下班谁会先到。


由于WebWiew攻击面较多,篇幅有限,就不一一阐述。我们将通过真实的案例的分析,着重介绍域名白名单绕过,JS恶意调用Native功能等安全问题。

之前,在一次处理紧急安全问题时,发现某款应用在手机上安装量突增。通过大数据分析后,发现突增的安装量基本来自应用A。安全应急小组的同学们重点锁定应用A,排查后发现应用A WebView相关的代码存在安全风险(同上述代码极为类似),唯一的区别是Native层开放下载和安装的能力。


接下来将分析哪些场景会出现安全问题。为更接近真实,小白对上述代码稍加改动,增加业务功能,如Java层开放下载安装功能。对于这段代码,如果加载的是一个恶意站点(www.evil.com),那么恶意站点可以通过JS层调用Java方法实现下载功能。示例代码如下:


 

 

对于该问题,小白很自然的想到的解决方法是,通过域名白名单控制,仅仅加载受信的域名,其他域名一律不加载。通过努力,小白上线了1.0修复版本,示意代码如下:


 

核心逻辑是在WebViewClient类的onPageStarted方法内部加入域名白名单的控制。这就好了吗?当然没有这么简单。如果首次加载的页面是白名单内页面,而白名单页面存在跳转的URL,而恰好跳转URL可被控制,那会怎么样呢?聪明的你一定猜到了。上述的修复方案就被绕过了。


那该怎么修复呢?

还记得前文介绍过shouldOverrideUrlLoading吗?该方法在重定向时会被回调。所以这里需要在

shouldOverrideUrlLoading回调中继续添加白名单的过滤。通过努力,小白上线了1.1修复版本

示意代码如下:

 

好了,首次加载、重定向都被控制住了,这次总算安全了吧。那倒未必,安不安全还需要看看白名单逻辑是否正确。小白可能会说这个还不简单,就是一个endsWith的事情嘛。通过5分钟努力,小白上线了1.0版本的域名白名单,示意代码如下:



这就安全了?

难道以site1.com结尾的域名就是白名单域名吗?显示不是。

比如可以注册一个evilsite1.com的域名,而这个域名和site1.com无任何关系,但却绕过白名单。这里问题产生的原因是使用未闭合的域名,可以通过在域名首部加上点完成闭合。


又经过一轮的修改,小白上线了1.1版本的域名白名单,示意代码如下:


 

经过几轮修正,目前这个版本看上去应该没什么问题了。真的没问题了么?如果java.net.URL可以得到绝对准确的host,那确实没问题了,但事实上,java.net.URL并不是完全可信,Android 8以下的系统存在URL("xxx").getHost()获取的值可以被反斜线(\)及特殊符号(@)绕过的情况,比如下图:


 

URL https://www.evil.com\\@www.site1.com/poc.html通过java.net.URL的getHost方法得到的host是www.site1.com,但实际上访问的却是www.evil.com域名。因此只要在域名为www.evil.com的服务器上放置/@www.site1.com/poc.html 这样一个文件,就可以绕过域名白名单,实现攻击。该问题主要原因是JDK自带的java.net.URL有问题,安全的方案是采用java.net.URI。通过java.net.URI对上一节中的绕过URL进行的测试,结果如下


 

可以看到畸形的URL会直接抛异常。经过努力,用java.net.URI代替java.net.URL,小白最终上线1.2版本的域名白名单,示意代码如下:


 

通过一遍遍的修改,至此白名单校验已趋于完善,本文也将接近尾声。


心法口诀——最后的忠告


本文中解决方案被一遍遍的修改,其实是一次次对抗过程。对此常常会被业务的同学问到,能否有完美的方案或是一次解决问题的方案。我们想说的是:安全是对抗的,是动态的,所以很不幸没有完美的解决方案,也没有一劳永逸的解决方案。没有一劳永逸的安全,而我们能做的是:规范开发流程,培养良好的编码风格,保持代码的敬畏之心,降低安全问题发生的概率。切莫天马行空的编码,好的代码一定是带着脚镣跳舞,而安全就是那副镣铐。


注:号外号外!vivo千镜实验室招人啦,点击阅读原文查看更多详情~


参考:

[1] BROWSER ARCHITECTURE: https://sangbui.com/sbfiles/BrowserArchitecture_ClientSide.pdf

[2] Web 浏览器相关的一些概念: 

https://keqingrong.cn/blog/2019-11-24-concepts-related-to-web-browsers

[3] 一文彻底搞懂安卓WebView白名单校验https://www.freebuf.com/articles/terminal/201407.html

— END —



1、小白训练营|Android常用的两种跳转协议概述学习

2、常见的匿名化隐私保护技术

3、小白训练营|Android应用签名介绍






您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存